Option Strict On
Option Explicit On

Imports System
Imports KadGen.MetadataWrapper
Imports KadGen.Utility2005
Imports System.Text

Public Class MorphToAssembly

   Dim mDefaults As Defaults

   Public Function CreateAssembly( _
         ByVal datastructure As DbsDataStructure, _
         ByVal mappingRoot As MapMappingRoot, _
         ByVal build As BuildBuild, _
         ByVal defaults As Defaults) _
         As GenAssembly
      Dim mapDataStructureName As String = datastructure.Name
      Dim name As String = mappingRoot.MapMapping.Name
      Dim assembly As New GenAssembly(mapDataStructureName, name)
      Utility.CopyCustomAttributes(mappingRoot, assembly, assembly)
      mDefaults = defaults
      ' TODO: Support NoGen 
      For Each objectDef As MapObjectDef In mappingRoot.MapMapping.MapObjectDefs
         Dim spName As String = Tools.ExpandWithObjectName(mappingRoot.RetrievePattern, objectDef.Name)
         Dim sp As DbsStoredProc = datastructure.GetSelectStoredProc(spName)
         If sp Is Nothing Then
            AddObject(assembly, datastructure, objectDef, build)
         Else
            AddObject(assembly, datastructure, sp, objectDef, build)
         End If
      Next
      'For Each table As DbsTable In datastructure.DbsTables
      '   Dim tableName As String = table.Name
      '   ' Check whether in mapping
      '   Dim objectDefs As MapObjectDefCollection = mappingRoot.GetByTableName(tableName)
      '   If objectDefs.Count = 0 Then
      '      AddObject(assembly, datastructure, table, Nothing)
      '   Else
      '      For Each objectDef As MapObjectDef In objectDefs
      '         AddObject(assembly, datastructure, table, objectDef)
      '      Next
      '   End If
      'Next
      Return assembly
   End Function

   Protected Overridable Sub AddObject( _
            ByVal assembly As GenAssembly, _
            ByVal datastructure As DbsDataStructure, _
            ByVal sp As DbsStoredProc, _
            ByVal objectdef As MapObjectDef, _
            ByVal build As BuildBuild)
      Dim collectionName As String = objectdef.PluralName
      Dim spSet As BuildSPSet = build.GetSPSetBySPName(sp.Name)
      If spSet Is Nothing Then
         ' TODO: Warn user and recover
      End If

      Dim newObject As New GenObject(assembly, objectdef.Name, objectdef.OriginalName, objectdef.SingularName, _
                  objectdef.PluralName, objectdef.DisplayName, objectdef.Caption, objectdef.PluralCaption, _
                  objectdef.DeletePriority, objectdef.InsertPriority, objectdef.TableName, collectionName, _
                  objectdef.Generate, objectdef.NoGenObject, objectdef.NoGenStoredProc, objectdef.NoGenUI, _
                  objectdef.ObjectName, objectdef.IsLookup, objectdef.NoChildren, True, _
                  True, True, True, True, objectdef.HasSelfJoin, objectdef.IsMultiParentChild, _
                  objectdef.TableNameColumnName, objectdef.PrimaryKeyColumnName)
      Utility.CopyCustomAttributes(objectdef, newObject, assembly)
      assembly.GenObjects.Add(newObject)

      Dim childNames As String = Nothing
      If sp.Attributes IsNot Nothing Then
         Dim childNamesAttribute As KADGen.MetadataWrapper.Attribute = sp.Attributes.GetItem("", "", "DirectNames")
         If childNamesAttribute IsNot Nothing Then
            childNames = childNamesAttribute.Value
         End If
      End If
      If String.IsNullOrEmpty(childNames) Then
         AddChildCollections(newObject, sp, spSet, objectdef)
      Else
         AddChildCollectionsFromExtendedProperties(newObject, sp, objectdef, childNames)
      End If

      AddStoredProcs(newObject, sp, spSet, datastructure, objectdef)
      AddFilterCriteria(newObject, sp, spSet, objectdef)
      AddSetFilterCriteria(newObject, sp, spSet, objectdef)
      AddForeignKeyTables(newObject, objectdef)
      AddPrivileges(newObject, spSet, objectdef)
      AddProperties(newObject, sp, spSet, objectdef)

      If objectdef.Attributes IsNot Nothing Then
         For Each attribute As KADGen.MetadataWrapper.Attribute In objectdef.Attributes
            newObject.AddCustomAttribute(attribute, False)
         Next
      End If
   End Sub

   Protected Overridable Sub AddObject( _
             ByVal assembly As GenAssembly, _
             ByVal datastructure As DbsDataStructure, _
             ByVal objectdef As MapObjectDef, _
             ByVal build As BuildBuild)
      ' TODO: Replace these true's with correct values, mostly for CSLA
      ' TODO: Support collection type from UI
      Dim collectionName As String = objectdef.PluralName

      ' TODO: We get here because we don't have a stored proc. These are the children that don't have parents. 
      Dim newObject As New GenObject(assembly, objectdef.Name, objectdef.OriginalName, objectdef.SingularName, _
                  objectdef.PluralName, objectdef.DisplayName, objectdef.Caption, objectdef.PluralCaption, _
                  objectdef.DeletePriority, objectdef.InsertPriority, objectdef.TableName, collectionName, _
                  objectdef.Generate, objectdef.NoGenObject, objectdef.NoGenStoredProc, objectdef.NoGenUI, _
                  objectdef.ObjectName, objectdef.IsLookup, objectdef.NoChildren, True, _
                  True, True, True, True, objectdef.HasSelfJoin, objectdef.IsMultiParentChild, _
                  objectdef.TableNameColumnName, objectdef.PrimaryKeyColumnName)
      Utility.CopyCustomAttributes(objectdef, newObject, assembly)
      assembly.GenObjects.Add(newObject)
      Dim viewOrTable As IViewOrTable = datastructure.GetViewOrTable(objectdef.TableName, objectdef.ViewName)
      Dim spSet As BuildSPSet = build.GetSPSet(objectdef.Name)
      If spSet Is Nothing Then
         ' TODO: Warn user and recover
      End If

      AddForeignKeyTables(newObject, objectdef)
      AddPrivileges(newObject, spSet, objectdef)
      AddProperties(newObject, objectdef)
      AddStoredProcs(newObject, Nothing, spSet, datastructure, objectdef)

      If objectdef IsNot Nothing AndAlso objectdef.Attributes IsNot Nothing Then
         For Each attribute As KADGen.MetadataWrapper.Attribute In objectdef.Attributes
            newObject.AddCustomAttribute(attribute, False)
         Next
      End If


   End Sub

   Protected Overridable Sub AddChildCollectionsFromExtendedProperties( _
         ByVal newObject As GenObject, _
         ByVal sp As DbsStoredProc, _
         ByVal objectDef As MapObjectDef, _
         ByVal childNames As String)

      Dim hierarchyPattern As String = sp.Attributes.GetItem("", "", "HierarchyPattern").Value
      If sp.Attributes IsNot Nothing Then
         Dim hierarchyPatternAttribute As KADGen.MetadataWrapper.Attribute = sp.Attributes.GetItem("", "", "HierarchyPattern")
         If hierarchyPatternAttribute IsNot Nothing Then
            hierarchyPattern = hierarchyPatternAttribute.Value
         End If
      End If
      Dim childNameArray As String() = childNames.Split(","c)
      Dim everythingFlag As ULong

      For i As Int32 = 1 To childNameArray.Length - 1
         Dim flag As ULong = 0
         Dim matchChild As MapChild = objectDef.MapChildren.GetChildByName(childNameArray(i).Trim())
         Dim logMessage64 As New Utility.LogMessage("Flags do not work with more than 64 children. There are " & objectDef.MapChildren.Count.ToString, LogId.Unknown, objectDef)
         Dim logMessageNoChild As New Utility.LogMessage("Can not find child: " & childNameArray(i), LogId.CanNotFindChild, objectDef)
         Dim logMessageNoChildObjectDef As New Utility.LogMessage("Can not find child object def for schild", LogId.CanNotFindChild, objectDef, matchChild)
         everythingFlag = AddChild(matchChild, objectDef, newObject, i, _
                  everythingFlag, logMessage64, logMessageNoChild, _
                  logMessageNoChildObjectDef, hierarchyPattern(i))
      Next
      newObject.GenChildCollections.EverythingFlag = everythingFlag
   End Sub

   Protected Overridable Sub AddChildCollections( _
            ByVal newObject As GenObject, _
            ByVal sp As DbsStoredProc, _
            ByVal spSet As BuildSPSet, _
            ByVal objectdef As MapObjectDef)
      Dim count As Int32 = -1
      Dim everythingFlag As ULong = 0

      ' TODO: After we rebuild structure, add NoChildInferrence
      If objectdef.IsLookup.GetValueOrDefault Or objectdef.MapMapping.NoInferrence.GetValueOrDefault Or objectdef.NoChildren.GetValueOrDefault Then
         Exit Sub
      End If

      ' Create a list and remove items as they are used. We need to use items that are even matches in order, but cannot rely on ordinal
      Dim children As MapChildCollection = objectdef.MapChildren.Clone

      For Each recordset As DbsRecordset In sp.DbsDataSet.DbsRecordsets
         If count = -1 Then
            count += 1
            ' The first recordset is always the base, the rest are descendents
            Continue For
         End If
         Dim logMessage64 As New Utility.LogMessage("Flags do not work with more than 64 returned recordsets. There are " & sp.DbsDataSet.DbsRecordsets.ToString, LogId.Unknown, sp, sp.DbsDataSet)
         Dim logMessageNoChild As New Utility.LogMessage("Can not find child for stored proc resultset", LogId.CanNotFindChild, objectdef, recordset)
         Dim logMessageNoChildObjectDef As New Utility.LogMessage("Can not find child object def for stored proc resultset", LogId.CanNotFindChild, objectdef, recordset)
         count += 1
         Dim matchChild As MapChild = GetBestChildMatch(recordset, children)
         everythingFlag = AddChild(matchChild, objectdef, newObject, count, _
                  everythingFlag, logMessage64, logMessageNoChild, _
                  logMessageNoChildObjectDef, Nothing)

      Next
      newObject.GenChildCollections.EverythingFlag = everythingFlag
   End Sub

   Private Function AddChild( _
            ByVal matchChild As MapChild, _
            ByVal objectDef As MapObjectDef, _
            ByVal newObject As GenObject, _
            ByVal count As Int32, _
            ByVal everythingFlag As ULong, _
            ByVal logMessage64 As Utility.LogMessage, _
            ByVal logMessageNoChild As Utility.LogMessage, _
            ByVal logMessageNoChildObjectDef As Utility.LogMessage, _
            ByVal isHierarchyString As String) _
            As ULong
      Dim flag As ULong
      Dim isHierarchy As Boolean = False
      If Not String.IsNullOrEmpty(isHierarchyString) Then
         isHierarchy = (isHierarchyString = "true")
      End If
      If count >= 64 Then
         If count = 64 Then
            ' Just output it once
            Utility.LogForUser(logMessage64)
         End If
         flag = 0
         everythingFlag = 0
      Else
         Console.WriteLine(count)
         flag = CULng(2 ^ count)
         everythingFlag += flag
      End If
      If matchChild Is Nothing Then
         Utility.LogForUser(logMessageNoChild)
      Else
         Dim matchObjectDef As MapObjectDef = matchChild.MapChildObjectDef
         If matchObjectDef Is Nothing Then
            Utility.LogForUser(logMessageNoChildObjectDef)
            Return Nothing
         End If
         Dim name As String
         Dim pluralName As String
         Dim objectName As String
         Dim tableName As String
         Dim caption As String
         If matchChild.IsHierarchy.GetValueOrDefault Then
            name = matchChild.Name
            pluralName = Tools.GetPlural(name)
            objectName = matchChild.ObjectName
            tableName = matchChild.TableName
            caption = Tools.GetCaption(name)
         Else
            name = matchObjectDef.Name
            pluralName = matchObjectDef.PluralName
            objectName = matchObjectDef.ObjectName
            tableName = matchObjectDef.TableName
            caption = matchObjectDef.Caption
         End If
         Dim uniqueName As String
         uniqueName = objectDef.MapChildren.GetUniqueChildName(matchChild)
         Dim casedName As String = matchChild.CasedName
         If uniqueName = matchChild.Name Then
            ' KAD: 20070301: While logical, the following code would not be hit becuase the
            '      Defaults logic sets the value. Leave in place for documentation 
            'If String.IsNullOrEmpty(matchChild.CasedName) Then
            '   matchChild.CasedName = Utility.GetCasedName(uniqueName)
            'End If
         Else
            If Utility.GetCasedName(matchChild.Name) = matchChild.CasedName Then
               ' Assume user's ORM did not specify the cased Name
               matchChild.CasedName = Utility.GetCasedName(uniqueName)
            End If
         End If
         Dim pluralUniqueName As String = Tools.GetPlural(uniqueName)
         Dim newChild As New GenChildCollection( _
               newObject.GenAssembly, name, _
               objectName, pluralName, _
               tableName, caption, _
               uniqueName, pluralUniqueName, flag, casedName, _
               matchChild.ChildFlag, matchChild.IsHierarchy)
         Utility.CopyCustomAttributes(matchChild, newChild, newObject.GenAssembly)
         newObject.GenChildCollections.Add(newChild)
         Dim ordinal As Int32 = 0
         'If matchObjectDef IsNot objectdef Then
         ' TODO: THis implies that child is not nothing, but does it prove it?
         For Each key As MapRelationKey In matchChild.MapRelationKeys
            ordinal += 1
            Dim newKey As New GenChildKeyField( _
                  newObject.GenAssembly, key.ChildColumn, key.ChildColumn, key.ChildTable, _
                  key.ChildObject, key.ChildProperty, ordinal, key.ParentColumn, _
                  key.ParentTable, key.ParentObject, key.ParentProperty)
            Utility.CopyCustomAttributes(key, newKey, newObject.GenAssembly)
            newChild.GenChildKeyFields.Add(newKey)
         Next
         For Each extraColumn As MapProperty In matchChild.MapExtraColumns
            Dim newExtra As New GenProperty( _
                  newObject.GenAssembly, extraColumn.Name, extraColumn.ColumnName, _
                  extraColumn.TableName, extraColumn.NETType, extraColumn.Caption, _
                  Tools.GetShortNetType(extraColumn.NETType), Tools.GetVbTypeAlias(extraColumn.NETType), _
                  Tools.GetCSharpTypeAlias(extraColumn.NETType), Tools.GetLegalVBType(extraColumn.NETType), _
                  extraColumn.AllowNulls, extraColumn.BaseNETType, _
                  extraColumn.Empty, extraColumn.IsAutoIncrement, extraColumn.IsComputed, _
                  extraColumn.IsPrimaryKey, extraColumn.ReadOnly, extraColumn.MaxLength, _
                  -1, extraColumn.OriginalName, extraColumn.SQLType, extraColumn.Unique, _
                  extraColumn.UseForDesc, 0, extraColumn.Default, _
                  extraColumn.DefaultVB, extraColumn.DefaultCSharp, extraColumn.Precision, _
                  extraColumn.Scale, extraColumn.CasedName)
            newChild.GenExtraColumns.Add(newExtra)
         Next
         'End If
      End If
      Return everythingFlag
   End Function

   Private Function GetBestChildMatch( _
            ByVal recordset As DbsRecordset, _
            ByVal children As MapChildCollection) _
            As MapChild
      Dim ret As MapObjectDef = Nothing
      Dim highChild As MapChild = Nothing
      Dim highScore As Int32 = 0
      Dim score As Int32 = 0
      For Each child As MapChild In children
         Dim childObjectDef As MapObjectDef = child.MapChildObjectDef
         score = GetScore(recordset, childObjectDef)
         If score = 100 Then
            highChild = child
            Exit For
         ElseIf score > 0 And score > highScore Then
            highScore = score
            highChild = child
         End If
      Next
      If highChild IsNot Nothing Then
         children.Remove(highChild)
         Return highChild
      Else
         Return Nothing
      End If
   End Function

   Private Function GetScore( _
            ByVal recordset As DbsRecordset, _
            ByVal objectDef As MapObjectDef) _
            As Int32
      ' For now, this is just the percent of total params that are in both. We don't care about the order
      Dim matching As Int32 = 0
      Dim notMatching As Int32 = 0
      Dim missing As Int32 = 0
      If objectDef Is Nothing Then
         Return 0
      End If
      For Each prop As MapProperty In objectDef.MapProperties
         If Not prop.IsComputed.GetValueOrDefault Then
            Dim dbsColumn As DbsColumn = recordset.GetColumn(prop.ColumnName)
            If dbsColumn Is Nothing Then
               missing += 1
            End If
         End If
      Next
      Return CInt(100 - 100 * (missing / objectDef.MapProperties.Count))
   End Function

   ' Keep this commented code : This is the old scoring mechanism that I may need to 
   ' return to as an option. 
   'Private Function GetScore( _
   '      ByVal recordset As DbsRecordset, _
   '      ByVal objectDef As MapObjectDef) _
   '      As Int32
   '   ' For now, this is just the percent of total params that are in both. We don't care about the order
   '   Dim matching As Int32 = 0
   '   Dim notMatching As Int32 = 0
   '   If objectDef Is Nothing Then
   '      Return 0
   '   End If
   '   For Each column As DbsColumn In recordset.DbsColumns
   '      If objectDef.GetPropertyByColumnName(column.Name) IsNot Nothing Then
   '         matching += 1
   '      Else
   '         If objectDef.MapProperties.GetProperty(column.Name) IsNot Nothing Then
   '            matching += 1
   '         Else
   '            notMatching += 1
   '         End If
   '      End If
   '   Next
   '   notMatching = notMatching + recordset.DbsColumns.Count - matching
   '   If notMatching = 0 Then
   '      Return 100
   '   Else
   '      ' Total matching divided by total columns in both files
   '      Return CInt(100 * (matching * 2) / (matching * 2 + notMatching))
   '   End If

   'End Function

   Protected Overridable Sub AddStoredProcs( _
            ByVal newObject As GenObject, _
            ByVal sp As DbsStoredProc, _
            ByVal spSet As BuildSPSet, _
            ByVal datastructure As DbsDataStructure, _
           ByVal objectdef As MapObjectDef)
      If spSet IsNot Nothing Then
         If objectdef.Generate.Contains("C") Then
            Dim create As BuildRunSP = spSet.GetCreateRunSP()
            If create IsNot Nothing Then
               Dim createName As String = create.Name
               Dim createSP As DbsStoredProc = datastructure.DbsStoredProcs(createName)
               If createSP Is Nothing Then
                  Utility.LogForUser(String.Format("Can not find create stored proc {0}", createName), LogId.CanNotFindStoredProc, objectdef)
               Else
                  newObject.GenCreateSP = New GenCreateSP( _
                              newObject.GenAssembly, createName, DateTime.Now.ToString, _
                              DateTime.Now.ToString)
                  AddParams(newObject.GenAssembly, newObject.GenCreateSP.GenParameters, objectdef, createSP, True)
               End If
            End If
         End If
         If objectdef.Generate.Contains("R") Then
            Dim retrieve As BuildRunSP = spSet.GetRetrieveRunSP()
            If retrieve IsNot Nothing Then
               Dim retrieveName As String = retrieve.Name
               Dim retrieveSP As DbsStoredProc = datastructure.DbsStoredProcs(retrieveName)
               If retrieveSP Is Nothing Then
                  Utility.LogForUser(String.Format("Can not find retrieve stored proc {0}", retrieveName), LogId.CanNotFindStoredProc, objectdef)
               Else
                  newObject.GenRetrieveSP = New GenRetrieveSP( _
                               newObject.GenAssembly, retrieveName, DateTime.Now.ToString, _
                               DateTime.Now.ToString)
                  AddParams(newObject.GenAssembly, newObject.GenRetrieveSP.GenParameters, objectdef, retrieveSP, True)
               End If
            End If
         End If
         If objectdef.Generate.Contains("U") Then
            Dim update As BuildRunSP = spSet.GetUpdateRunSP()
            If update IsNot Nothing Then
               Dim updateName As String = update.Name
               Dim updateSP As DbsStoredProc = datastructure.DbsStoredProcs(updateName)
               If updateSP Is Nothing Then
                  Utility.LogForUser(String.Format("Can not find update stored proc {0}", updateName), LogId.CanNotFindStoredProc, objectdef)
               Else
                  newObject.GenUpdateSP = New GenUpdateSP( _
                               newObject.GenAssembly, updateName, DateTime.Now.ToString, _
                               DateTime.Now.ToString)
                  AddParams(newObject.GenAssembly, newObject.GenUpdateSP.GenParameters, objectdef, updateSP, True)
               End If
            End If
         End If
         If objectdef.Generate.Contains("D") Then
            Dim delete As BuildRunSP = spSet.GetDeleteRunSP()
            If delete IsNot Nothing Then
               Dim deleteName As String = delete.Name
               Dim deleteSP As DbsStoredProc = datastructure.DbsStoredProcs(deleteName)
               If deleteSP Is Nothing Then
                  Utility.LogForUser(String.Format("Can not find delete stored proc {0}", deleteName), LogId.CanNotFindStoredProc, objectdef)
               Else
                  newObject.GenDeleteSP = New GenDeleteSP( _
                                       newObject.GenAssembly, deleteName, DateTime.Now.ToString, _
                                       DateTime.Now.ToString)
                  AddParams(newObject.GenAssembly, newObject.GenDeleteSP.GenParameters, objectdef, deleteSP, True)
               End If
            End If
         End If
         If objectdef.Generate.Contains("S") Then
            Dim setSelect As BuildRunSP = spSet.GetSetSelectRunSP()
            If setSelect IsNot Nothing Then
               Dim setSelectName As String = setSelect.Name
               Dim setSelectSP As DbsStoredProc = datastructure.DbsStoredProcs(setSelectName)
               If setSelectSP Is Nothing Then
                  Utility.LogForUser(String.Format("Can not find set select stored proc {0}", setSelectName), LogId.CanNotFindStoredProc, objectdef)
               Else
                  newObject.GenSetSelectSP = New GenSetSelectSP( _
                                         newObject.GenAssembly, setSelectName, DateTime.Now.ToString, _
                                         DateTime.Now.ToString)
               End If
            End If
         End If
      End If


      'newObject.GenUseSPSet = New GenUseSPSet(newObject.GenAssembly, objectdef.Name, objectdef.TableName)
      'Dim name As String
      'If objectdef.Generate.Contains("C") Then
      '   name = Utility.ResolveName(Me.mDefaults.CreatePattern, objectdef.Name)
      '   newObject.GenCreateSP = New GenCreateSP( _
      '               newObject.GenAssembly, name, DateTime.Now.ToString, _
      '               DateTime.Now.ToString)
      '   AddSaveParams(newObject.GenAssembly, newObject.GenCreateSP.GenParameters, objectdef, True)
      'End If
      'If objectdef.Generate.Contains("R") Then
      '   name = Utility.ResolveName(Me.mDefaults.RetrievePattern, objectdef.Name)
      '   newObject.GenRetrieveSP = New GenRetrieveSP( _
      '               newObject.GenAssembly, name, DateTime.Now.ToString, _
      '               DateTime.Now.ToString)
      '   AddPrimaryKeyParams(newObject.GenAssembly, newObject.GenRetrieveSP.GenParameters, objectdef)
      '   'AddSPRecordset(table, name)
      'End If
      'If objectdef.Generate.Contains("U") Then
      '   name = Utility.ResolveName(Me.mDefaults.UpdatePattern, objectdef.Name)
      '   newObject.GenUpdateSP = New GenUpdateSP( _
      '               newObject.GenAssembly, name, DateTime.Now.ToString, _
      '               DateTime.Now.ToString)
      '   AddSaveParams(newObject.GenAssembly, newObject.GenUpdateSP.GenParameters, objectdef, False)
      'End If
      'If objectdef.Generate.Contains("D") Then
      '   name = Utility.ResolveName(Me.mDefaults.DeletePattern, objectdef.Name)
      '   newObject.GenDeleteSP = New GenDeleteSP( _
      '               newObject.GenAssembly, name, DateTime.Now.ToString, _
      '               DateTime.Now.ToString)
      '   AddPrimaryKeyParams(newObject.GenAssembly, newObject.GenDeleteSP.GenParameters, objectdef)
      'End If
      'If objectdef.Generate.Contains("S") Then
      '   name = Utility.ResolveName(Me.mDefaults.SetSelectPattern, objectdef.Name)
      '   newObject.GenSetSelectSP = New GenSetSelectSP( _
      '               newObject.GenAssembly, name, DateTime.Now.ToString, _
      '               DateTime.Now.ToString)
      'End If
   End Sub

   Protected Overridable Sub AddFilterCriteria( _
            ByVal newObject As GenObject, _
            ByVal sp As DbsStoredProc, _
            ByVal spSet As BuildSPSet, _
            ByVal objectdef As MapObjectDef)
      For Each filter As MapSearchCriteria In objectdef.MapObjectSearchCriterias
         If Not filter.IsPrimaryKeyCriteria.GetValueOrDefault Then
            ' I'm not crazy about the approach of using the stored proc name, but I don't currently have a way for the 
            ' programmer to offer an alternate name, so can't see putting a lot of work into this flexibility
            Dim retrieveName As String = Utility.GetRetrieveSPName(Me.mDefaults, objectdef, filter)
            Dim newFilter As New GenFilterCriteria(newObject.GenAssembly, filter.Name, retrieveName, filter.Caption, filter.IsPrimaryKeyCriteria)
            Utility.CopyCustomAttributes(filter, newFilter, newObject.GenAssembly)
            newObject.GenFilterCriterias.Add(newFilter)
            For Each criterion As MapSearchCriterion In filter.MapSearchCriterions
               Dim newCriterion As New GenFilterCriterion( _
                     newObject.GenAssembly, criterion.Name, criterion.TableName, _
                     criterion.Caption, criterion.ColumnName, criterion.NETType)
               Utility.CopyCustomAttributes(criterion, newCriterion, newObject.GenAssembly)
               newFilter.GenFilterCriterions.Add(newCriterion)
            Next
            Dim retrieveSP As DbsStoredProc = sp.DbsDataStructures(0).DbsStoredProcs(retrieveName)
            If retrieveSP Is Nothing Then
               Utility.LogForUser(String.Format("Can not find stored procedure named {0}", retrieveName), _
                        LogId.CanNotFindStoredProc, objectdef, filter)
            Else
               ' This has been removed because I don't have any working scnearios where the parameters would not
               ' match the criterion and don't think we'll have the informaiton to make variations work. 
               'AddParams(newObject.GenAssembly, newFilter.GenParameters, objectdef, retrieveSP, True)
            End If
         End If
      Next
   End Sub

   Protected Overridable Sub AddSetFilterCriteria( _
         ByVal newObject As GenObject, _
         ByVal sp As DbsStoredProc, _
         ByVal spSet As BuildSPSet, _
         ByVal objectdef As MapObjectDef)
      For Each filter As MapSearchCriteria In objectdef.MapSetSearchCriterias
         If Not filter.IsPrimaryKeyCriteria.GetValueOrDefault Then
            ' I'm not crazy about the approach of using the stored proc name, but I don't currently have a way for the 
            ' programmer to offer an alternate name, so can't see putting a lot of work into this flexibility
            Dim retrieveName As String = Utility.GetSetSelectSPName(Me.mDefaults, objectdef, filter)
            Dim newFilter As New GenSetFilterCriteria(newObject.GenAssembly, filter.Name, retrieveName, filter.Caption, filter.IsPrimaryKeyCriteria)
            Utility.CopyCustomAttributes(filter, newFilter, newObject.GenAssembly)
            newObject.GenSetFilterCriterias.Add(newFilter)
            For Each criterion As MapSearchCriterion In filter.MapSearchCriterions
               Dim newCriterion As New GenFilterCriterion( _
                     newObject.GenAssembly, criterion.Name, criterion.TableName, _
                     criterion.Caption, criterion.ColumnName, criterion.NETType)
               Utility.CopyCustomAttributes(criterion, newCriterion, newObject.GenAssembly)
               newFilter.GenFilterCriterions.Add(newCriterion)
            Next
            Dim retrieveSP As DbsStoredProc = sp.DbsDataStructures(0).DbsStoredProcs(retrieveName)
            If retrieveSP Is Nothing Then
               Utility.LogForUser(String.Format("Can not find stored procedure named {0}", retrieveName), _
                        LogId.CanNotFindStoredProc, objectdef, filter)
            Else
               ' This has been removed because I don't have any working scnearios where the parameters would not
               ' match the criterion and don't think we'll have the informaiton to make variations work. 
               'AddParams(newObject.GenAssembly, newFilter.GenParameters, objectdef, retrieveSP, True)
            End If
         End If
      Next
   End Sub

   Protected Overridable Sub AddForeignKeyTables( _
         ByVal newObject As GenObject, _
         ByVal objectdef As MapObjectDef)
      For Each foreignKey As MapForeignKey In objectdef.MapForeignKeys
         Dim uniqueName As String
         Dim otherKeys As MapForeignKeyCollection = GetForeignKeysWithName(foreignKey, objectdef.MapForeignKeys)

         uniqueName = foreignKey.Name
         If otherKeys.Count > 0 Then
            Dim keyFields As New List(Of String)
            For Each other As MapForeignKey In otherKeys
               For i As Int32 = 0 To other.MapRelationKeys.Count - 1
                  Dim test As String = other.MapRelationKeys(i).ChildProperty
                  If Not foreignKey.IncludesKey(test) Then
                     If Not keyFields.Contains(test) Then
                        keyFields.Add(test)
                     End If
                  End If
               Next
               If keyFields.Count = 0 Then
                  ' TODO: Tell user there are redundant foreign keys. The user may receive redundant messages, but I'm not too worried about that.
               End If
            Next
            For Each field As String In keyFields
               uniqueName += field
            Next
         End If


         Dim newForeignKeyTable As New GenForeignKeyTable(newObject.GenAssembly, foreignKey.Name, uniqueName)
         newObject.GenForeignKeyTables.Add(newForeignKeyTable)
         For Each key As MapRelationKey In foreignKey.MapRelationKeys
            Dim newKey As New GenForeignKey( _
                     newObject.GenAssembly, key.ChildColumn, key.ChildTable, _
                     key.ChildObject, key.ChildProperty, key.ParentProperty, key.ParentTable, _
                     key.ParentObject, key.ParentProperty)
            Utility.CopyCustomAttributes(key, newKey, newObject.GenAssembly)
            newForeignKeyTable.GenForeignKeys.Add(newKey)
         Next

         Dim child As MapChild = foreignKey.MapChild
         If child IsNot Nothing Then
            For Each extraProp As MapProperty In child.MapExtraColumns
               Dim newProp As New GenProperty(newObject.GenAssembly, extraProp.Name, extraProp.ColumnName, _
                     extraProp.TableName, extraProp.NETType, extraProp.Caption, _
                     Tools.GetShortNetType(extraProp.NETType), _
                     Tools.GetVbTypeAlias(extraProp.NETType), _
                     Tools.GetCSharpTypeAlias(extraProp.NETType), _
                     Tools.GetLegalVBType(extraProp.NETType), _
                     extraProp.AllowNulls, extraProp.NETType, extraProp.Empty, _
                     extraProp.IsAutoIncrement, extraProp.IsComputed, _
                     extraProp.IsPrimaryKey, extraProp.ReadOnly, extraProp.MaxLength, _
                     extraProp.Ordinal, extraProp.OriginalName, extraProp.SQLType, _
                     extraProp.Unique, extraProp.UseForDesc, 0, extraProp.Default, _
                     extraProp.DefaultVB, extraProp.DefaultCSharp, extraProp.Precision, _
                     extraProp.Scale, extraProp.CasedName)
               newForeignKeyTable.GenExtraColumns.Add(newProp)
            Next
         End If
      Next
   End Sub

   Private Function GetForeignKeysWithName(ByVal fk As MapForeignKey, ByVal foreignKeys As MapForeignKeyCollection) As MapForeignKeyCollection
      Dim ret As New MapForeignKeyCollection
      For Each fkTest As MapForeignKey In foreignKeys
         If fkTest IsNot fk AndAlso fkTest.Name = fk.Name Then
            ret.Add(fkTest)
         End If
      Next
      Return ret
   End Function

   Protected Overridable Sub AddPrivileges( _
            ByVal newObject As GenObject, _
            ByVal spSet As BuildSPSet, _
            ByVal objectDef As MapObjectDef)
      For Each mapPrivilege As MapPrivilege In objectDef.MapObjectPrivileges
         newObject.GenClassPrivileges.Add(New GenClassPrivilege(newObject.GenAssembly, mapPrivilege.Grantee, mapPrivilege.Rights))
      Next
   End Sub

   Protected Overridable Sub AddProperties( _
            ByVal newObject As GenObject, _
            ByVal sp As DbsStoredProc, _
            ByVal spSet As BuildSPSet, _
            ByVal objectDef As MapObjectDef)
      If sp.DbsDataSet.DbsRecordsets.Count = 0 Then
         Utility.LogForUser("Stored procedure failed to return recordsets", LogId.CanNotCreateName, objectDef, spSet, sp)
         Exit Sub
      End If
      Dim rs As DbsRecordset = sp.DbsDataSet.DbsRecordsets(0)
      ' We copy to list, then use so redundant columns just need to be in correct order
      Dim properties As MapPropertyCollection = objectDef.MapProperties.Clone()
      For Each column As DbsColumn In rs.DbsColumns
         Dim prop As MapProperty = properties.GetPropertyByPreferredName(objectDef.TableName, column.Name, column.Name)
         If prop Is Nothing Then
            Utility.LogForUser("Can not find property for stored procedure column", LogId.CanNotFindProperty, objectDef, sp, column)
         End If
         ' TODO: extend this to support calculated columns
         If prop IsNot Nothing Then
            properties.Remove(prop)
            Dim vbTypeAlias As String = Tools.GetVbTypeAlias(prop.NETType)
            Dim newProp As New GenProperty( _
                  newObject.GenAssembly, prop.Name, prop.ColumnName, _
                  prop.TableName, prop.NETType, prop.Caption, Tools.GetShortNetType(prop.NETType), _
                  vbTypeAlias, Tools.GetCSharpTypeAlias(prop.NETType), _
                  Tools.GetLegalVBType(prop.NETType), prop.AllowNulls, prop.BaseNETType, _
                  prop.Empty, prop.IsAutoIncrement, prop.IsComputed, prop.IsPrimaryKey, _
                  prop.ReadOnly, prop.MaxLength, prop.Ordinal, prop.OriginalName, _
                  prop.SQLType, prop.Unique, prop.UseForDesc, objectDef.MapForeignKeys.CountForKey(prop.Name), _
                  prop.Default, prop.DefaultVB, prop.DefaultCSharp, prop.Precision, _
                  prop.Scale, prop.CasedName)
            Utility.CopyCustomAttributes(prop, newProp, newObject.GenAssembly)
            newObject.GenProperties.Add(newProp)

            If prop.Attributes IsNot Nothing Then
               For Each attribute As KADGen.MetadataWrapper.Attribute In prop.Attributes
                  newProp.AddCustomAttribute(attribute, False)
               Next
            End If

            SetPropertyPrivileges(newProp, prop)
         End If
      Next

      'For Each prop As MapProperty In objectdef.MapProperties
      '   Dim vbTypeAlias As String = Tools.GetVbTypeAlias(prop.NETType)
      '   newObject.GenProperties.Add(New GenProperty( _
      '         newObject.GenAssembly, prop.Name, prop.ColumnName, _
      '         prop.TableName, prop.NETType, prop.Caption, Tools.GetShortNetType(prop.NETType), _
      '         vbTypeAlias, Tools.GetCSharpTypeAlias(prop.NETType), _
      '         Tools.GetLegalVBType(prop.NETType), prop.AllowNulls, prop.BaseNETType, _
      '         prop.Empty, prop.IsAutoIncrement, prop.IsComputed, prop.IsPrimaryKey, _
      '         prop.ReadOnly, prop.MaxLength, prop.Ordinal, prop.OriginalName, _
      '         prop.SQLType, prop.Unique, prop.UseForDesc, objectdef.MapForeignKeys.CountForKey(prop.Name), _
      '         prop.Default, prop.DefaultVB, prop.DefaultCSharp, prop.Precision, _
      '         prop.Scale))
      'Next
   End Sub

   Protected Overridable Sub AddProperties( _
         ByVal newObject As GenObject, _
         ByVal objectDef As MapObjectDef)
      ' We copy to list, then use so redundant columns just need to be in correct order
      For Each prop As MapProperty In objectDef.MapProperties
         If Not String.IsNullOrEmpty(prop.ColumnName) Or mDefaults.UseUnbackedInSetSelect Then

            ' TODO: extend this to support calculated columns
            Dim vbTypeAlias As String = Tools.GetVbTypeAlias(prop.NETType)
            Dim newProp As New GenProperty( _
                  newObject.GenAssembly, prop.Name, prop.ColumnName, _
                  prop.TableName, prop.NETType, prop.Caption, Tools.GetShortNetType(prop.NETType), _
                  vbTypeAlias, Tools.GetCSharpTypeAlias(prop.NETType), _
                  Tools.GetLegalVBType(prop.NETType), prop.AllowNulls, prop.BaseNETType, _
                  prop.Empty, prop.IsAutoIncrement, prop.IsComputed, prop.IsPrimaryKey, _
                  prop.ReadOnly, prop.MaxLength, prop.Ordinal, prop.OriginalName, _
                  prop.SQLType, prop.Unique, prop.UseForDesc, objectDef.MapForeignKeys.CountForKey(prop.Name), _
                  prop.Default, prop.DefaultVB, prop.DefaultCSharp, prop.Precision, _
                  prop.Scale, prop.CasedName)
            Utility.CopyCustomAttributes(prop, newProp, newObject.GenAssembly)
            newObject.GenProperties.Add(newProp)

            If prop IsNot Nothing AndAlso prop.Attributes IsNot Nothing Then
               For Each attribute As KADGen.MetadataWrapper.Attribute In prop.Attributes
                  newProp.AddCustomAttribute(attribute, False)
               Next
            End If

            SetPropertyPrivileges(newProp, prop)
         End If
      Next

   End Sub

   Private Sub SetPropertyPrivileges( _
            ByVal newProp As GenProperty, _
            ByVal prop As MapProperty)
      For Each mapPrivilege As MapColumnPrivilege In prop.MapColumnPrivileges
         newProp.GenPropertyPrivileges.Add(New GenPropertyPrivilege( _
                  newProp.GenAssembly, mapPrivilege.Grantee, _
                  mapPrivilege.Visible, mapPrivilege.Edit))
      Next
   End Sub


   Private Sub AddParams( _
             ByVal assembly As GenAssembly, _
             ByVal params As GenParameterCollection, _
             ByVal objectDef As MapObjectDef, _
             ByVal sp As DbsStoredProc, _
             ByVal isCreate As Boolean)
      Dim ordinal As Int32 = 1
      For Each spParam As DbsStoredProcParameter In sp.DbsStoredProcParameters
         Dim caption As String = Nothing
         Dim baseNetType As String = Nothing
         Dim netType As String = Nothing
         Dim shortNetType As String = Nothing
         Dim propertyName As String = spParam.Name.Substring(1)
         Dim prop As MapProperty = objectDef.MapProperties.GetProperty(propertyName)
         Dim casedName As String = Nothing
         If prop IsNot Nothing Then
            caption = prop.Caption
            baseNetType = prop.BaseNETType
            netType = prop.NETType
            shortNetType = Tools.GetShortNetType(prop.NETType)
            propertyName = prop.Name
            casedName = prop.CasedName
         End If
         params.Add(New GenParameter(assembly, spParam.Name, spParam.Direction, spParam.MaxLength, _
                  caption, spParam.Type, baseNetType, netType, shortNetType, propertyName, casedName))

      Next
   End Sub

   'Private Sub AddSaveParams( _
   '            ByVal assembly As GenAssembly, _
   '            ByVal params As GenParameterCollection, _
   '            ByVal objectDef As MapObjectDef, _
   '            ByVal isCreate As Boolean)
   '   Dim ordinal As Int32 = 1
   '   For Each prop As MapProperty In objectDef.MapProperties
   '      ' Support join base here
   '      If prop.TableName = objectDef.TableName Then
   '         If Not prop.ReadOnly Then
   '            Dim direction As String = "IN"
   '            If prop.IsAutoIncrement And isCreate Then
   '               direction = "OUT"
   '            End If
   '            ' The caption is the ordinal to match legacy output
   '            params.Add(New GenParameter( _
   '                  assembly, prop.Name, direction, _
   '                  prop.MaxLength, ordinal.ToString, prop.SQLType, _
   '                  prop.BaseNETType, prop.NETType, Tools.GetShortNetType(prop.NETType)))
   '            ordinal += 1
   '         End If
   '      End If
   '   Next

   'End Sub

   'Private Sub AddPrimaryKeyParams( _
   '          ByVal assembly As GenAssembly, _
   '          ByVal params As GenParameterCollection, _
   '          ByVal objectDef As MapObjectDef)
   '    Dim ordinal As Int32 = 1
   '    For Each prop As MapProperty In objectDef.MapProperties
   '        If prop.TableName = objectDef.TableName Then
   '            If prop.IsPrimaryKey Then
   '                Dim shortNetType As String = prop.NETType
   '                If shortNetType.ToLower.StartsWith("system.") Then
   '                    shortNetType = shortNetType.Substring(7)
   '                End If
   '                ' The caption is the ordinal to match legacy output
   '                Dim param As New GenParameter( _
   '                      assembly, prop.Name, "IN", _
   '                      prop.MaxLength, ordinal.ToString, prop.SQLType, _
   '                      prop.BaseNETType, prop.NETType, shortNetType, prop.Name)
   '                Utility.CopyCustomAttributes(prop, param, assembly)
   '                params.Add(param)
   '                ordinal += 1
   '            End If
   '        End If
   '    Next

   'End Sub

End Class
